let w = null
//6.封装一个Watcher类
class Watcher{
    //Watcher实例化的时候接受两个参数,一个是key所在的对象 一个是key
    constructor (obj,key){
        this.obj = obj
        this.key = key 
        //当我要获取值的时候,把当前的watcher保存在全局变量上
        w = this
        //在get中用完W,就把W给设置回去
        this.get()
        w = null
    }
    //当组件内new Watcher的时候就是想得到一个值,所以封装一个get方法拿值
    get(){
        //一旦这样拿值,就会直接进入当前key的响应式的getter函数中
        return this.obj[this.key];
    }
    //在watcher内部封装一个update方法,当通知我更新的时候,直接调用当前的update方法
    update(){
        console.log(
            "gogogo,有人通知我了,我重新去请求数据是:" +
              this.get() +
              ",然后开始重新渲染视图!!!!"
          );
    }
}
//5.定义一个Dep类,用来收集依赖 和 通知更新
class Dep{
    constructor(){
        //每次实例化Dep就会创建一个空的数组,然后用来保存所有的watcher
        this.subs = []
    }
    depend(watcher){
        this.subs.push(watcher)
        console.log(
            "每次访问我的数据的时候,我就把watcher收集到dep.subs这个数组中",
            this.subs
          );
    }
    notify(){
        console.log("通知所有的watcher 重新加载新的数据");
        this.subs.forEach(watcher => {
            watcher.update()
        })
    }
}
//4.封装defineReactive函数，主要是为了对单个数据做数据劫持（数据劫持的核心代码）
function defineReactive(_data,key,value){
    //把value值再次交给observe方法,进行深层次的数据响应式(不需要判断,因为observe中有判断,如果是基本类型,则直接return,我们继续向下)
    observe(value)
    console.log(value);
    //每次对一个值进行响应式的时候,实例化一个Dep类
    const dep = new Dep()
    Object.defineProperty(_data,key,{
        get(){
            console.log("正在获取" + key + "的值", "收集所有依赖项watcher");
            //当这个数据被请求的时候,把观察者watcher收集起来到dep.subs中
            //我们可能不在模板解析的时候(也就是不是Watcher来要数据),我们需要做判断处理
            if(w){
                dep.depend(w)
            }
            return value;
        },
        set(newVal){
            if(newVal === value) return ;
            console.log("正在设置" + key + "的值", "通知所有的watcher进行更新");
            value = newVal
            //当我的响应式数据被修改的时候,我要通知所有dep.subs里的watcher 进行更新
            dep.notify()
        }
    })
}
//3. 封装一个Observer类，主要用来处理发布订阅模式的核心操作（响应式操作：收集依赖，通知更新）
class Observer{
    //构造器接受的是要被响应式处理的对象
    constructor(_data){
        //把这个对象上放在实例上,将来在任何原型方法中都可以访问到这个实例上的数据
        this._data = _data
        console.log(_data);
        //判断当前的_data是对象还是数组
        if(Array.isArray(_data)){
            this.observeArray(_data)
        }else{
            this.walk(_data)
        }
    }
    //在Observer的原型对象上封装一个对数组操作的方法
    observeArray(_data){
        //当现在被操作的是数组的时候,我们不能直接使用defineProperty进行操作,但是我们要对数组内部的对象进行响应式操作
        _data.forEach(item => {
            //把数组的值交给observe函数重新开始监听
            observe(item)
        });
    }
    //在Observer的原型对象上封装一个对对象操作的方法
    walk(_data){
        for(let key of Object.keys(_data)){
            //把每一个数据的响应式核心代码交给defineReactive函数处理
            defineReactive(_data,key,_data[key])
        }
    }
}

//2.封装一个observe函数，接收_data,遍历_data的值，一个个的进行数据劫持
function observe(_data){
    if(typeof _data !== 'object' || _data === null) return ;
    new Observer(_data)
    console.log(_data);
}

//1.封装一个Vue构造函数
function Vue(options){
    this._data = options.data
    //数据代理
    for(let key of Object.keys(this._data)){
        Object.defineProperty(this,key,{
            get(){
                return this._data[key]
            },
            set(newVal){
                this._data[key] = newVal
            }
        })
    }
    //拿到_data的数据开始进行响应式操作，我们首先把响应式操作的代码放在observe函数中
    observe(this._data)
    console.log(this._data);
}

const vm = new Vue ({
    data:{
        count:1,
        curse:{
            name:'easy',
            age:18
        }
    }
})

function huoqu (){
    //模拟模板解析,每个组件都会实例化一个watcher 然后开始观察数据
    new Watcher(vm,'count')
}
function shezhi() {
    vm.count = 3;
}
huoqu();
// shezhi();
console.log(vm);
